import { sSample } from './math'
import { TEMP, applyBasicTransform } from './box'
import { handleMatch } from './handleMatch'
import { XYFromRayLoops } from './rayLoops'
import { Matrix4 } from './_imports'
import type { MagizTypes, StyleParsed, Temp } from './_imports'

export { handleExtrude }

/** 按 parsed.block 挤出平面 */
function handleExtrude(
  result: MagizTypes.DataJSON.ModelData,
  parsedStyle: Pick<StyleParsed.FloorResult, 'extrude' | 'elevations'>,
  rayLoops: Temp.Ray[][],
  /** 通过全局变量控制是否拟合 */
  globalMatchWidth: number
) {
  const { extrude, elevations } = parsedStyle
  extrude.forEach((params) => {
    const { color, trans, height, along, thickness } = params
    const { index, glass } = sSample(color)!
    const matrix = new Matrix4()
    if (thickness) {
      // 先判断是否用box生成围墙
      const moveY = thickness < 0 ? -0.5 : 0.5
      const moveZ = height < 0 ? -0.5 : 0.5
      const saveAs = result.instanced[glass ? 'boxGlass' : 'box']
      rayLoops.forEach((rayLoop) => {
        rayLoop.forEach((ray) => {
          const boxMatrix = new Matrix4()
            .premultiply(TEMP.makeTranslation(0.5, moveY, moveZ))
            .premultiply(
              TEMP.makeScale(ray.direction.length(), Math.abs(thickness), Math.abs(height))
            )
            .premultiply(TEMP.makeRotationZ(ray.direction.angle()))
          applyBasicTransform(params, boxMatrix)

          elevations.forEach((elevation) => {
            const m = boxMatrix.clone()
            m.premultiply(TEMP.makeTranslation(ray.start.x, ray.start.y, elevation))
            saveAs.matrices.push(m.toArray())
            saveAs.colors.push(index)
          })
        })
      })
    } else if (globalMatchWidth > 0) {
      const matchParam: StyleParsed.Match = {
        simplify: 1,
        sandwich: 0,
        along,
        ctrlMatch: undefined,
        array: [
          {
            count: 1,
            unitDepth: globalMatchWidth,
            flexHeight: params.height,
            color,
            trans,
            shrink: undefined,
          },
        ],
      }
      // 再判断通过全局变量是否用box挤出平面
      handleMatch(result, { match: [matchParam], elevations }, rayLoops)
    } else {
      const saveTo = result.extruded[glass ? 'glass' : 'solid']
      let saveFound = saveTo.find((e) => isSameLoops(e.loops, rayLoops))
      if (!saveFound) {
        saveFound = { loops: XYFromRayLoops(rayLoops), matrices: [], colors: [] }
        saveTo.push(saveFound)
      }
      const saveAs = saveFound
      if (height < 0) matrix.premultiply(TEMP.makeTranslation(0, 0, -1))
      matrix.premultiply(TEMP.makeScale(1, 1, Math.abs(height)))
      applyBasicTransform(params, matrix)
      elevations.forEach((elevation) => {
        saveAs.matrices.push(
          matrix.clone().premultiply(TEMP.makeTranslation(0, 0, elevation)).toArray()
        )
        saveAs.colors.push(index)
      })
    }
  })
}

function isSameLoops(loopsA: MagizTypes.XY[][], loopsB: Temp.Ray[][]): boolean {
  return loopsA.every((loopA, i) => {
    const loopB = loopsB[i]
    return loopB
      ? loopA.every((pointA, j) => {
          const rayB = loopB[j]
          return rayB && pointA.x === rayB.start.x && pointA.y === rayB.start.y
        })
      : false
  })
}
